package georah.map.tools
{

    import flash.geom.Point;
    
    import georah.carto.Extent;
    import georah.carto.PanZoom;
    import georah.carto.SRS;
    import georah.carto.geometries.OGCPoint;
    import georah.map.MapValue;

    /**
     *
     * @author yuanm
     */
    public class Navigation extends MapTool
    {
        public static const PAN:int = 0;

        public static const PAN_DOWN:int = 270;

        public static const PAN_LEFT:int = 180;

        public static const PAN_RIGHT:int = 0;

        public static const PAN_UP:int = 90;

        public static const ZOOM_IN:int = 1;

        public static const ZOOM_OUT:int = -1;

        private var _dragPanFrom:OGCPoint = null;

        private var _dragZoomFrom:OGCPoint = null;

        private var _panStep:Number = 8;

        private var _zoomStep:Number = 0.8;

        public final function dragPan(fromWhere:OGCPoint, toWhere:OGCPoint):void
        {
            var camera:PanZoom = PanZoom(map.camera).clone();
            camera.pan(toWhere.x - fromWhere.x, toWhere.y - fromWhere.y);
            map.camera = camera;
        }

        public final function dragPanStart(where:OGCPoint):void
        {
            if (!map)
            {
                return;
            }
            _dragPanFrom = where;
            map.layers.startDrag();
        }

        public final function dragPanStop(where:OGCPoint):void
        {
            if (!map)
            {
                return;
            }

            dragPan(_dragPanFrom, where);
            _dragPanFrom = null;
            map.layers.stopDrag();
        }

        public final function dragZoom(fromWhere:OGCPoint, toWhere:OGCPoint, zoomType:int):void
        {
            var zoom:Number = 1.0;
            var rectWidth:Number = Math.abs(toWhere.x - fromWhere.x);
            var rectHeight:Number = Math.abs(toWhere.y - fromWhere.y);
            var envelope:Extent = map.envelope;
            if (rectWidth / rectHeight > map.width / map.height)
            {
                zoom = rectWidth / envelope.width;
            }
            else
            {
                zoom = rectHeight / envelope.height;
            }
            var scale:Number = 1.0;
            switch (zoomType)
            {
                case Navigation.ZOOM_IN:
                    scale = zoom;
                    break;
                case Navigation.ZOOM_OUT:
                    scale = 1.0 / zoom;
                    break;
            }
            var camera:PanZoom = PanZoom(map.camera).clone();
            camera.center = new OGCPoint(fromWhere.SRID, 0.5 * (fromWhere.x + toWhere.x), 0.5 * (fromWhere.y + toWhere.y));
            camera.zoom(scale);
            var pixel:Point = map.fromWhereToPixel(camera.center);
            map.layers.x += 0.5 * map.width - pixel.x;
            map.layers.y += 0.5 * map.height - pixel.y;
            map.camera = camera;
        }

        public final function dragZoomStart(where:OGCPoint):void
        {
            if (!map)
            {
                return;
            }
            _dragZoomFrom = where;
        }

        public final function dragZoomStop(where:OGCPoint, zoomType:int):void
        {
            if (!map)
            {
                return;
            }

            dragZoom(_dragZoomFrom, where, zoomType);
            _dragZoomFrom = null;
        }

        virtual override public function get keyboardLocked():Boolean
        {
            return false;
        }

        virtual override public function get mouseLocked():Boolean
        {
            return false;
        }

        public final function panTo(where:OGCPoint, zoomType:int=0):void
        {
            if (!map)
            {
                return;
            }
            var camera:PanZoom = PanZoom(map.camera).clone();
            switch (zoomType)
            {
                case Navigation.ZOOM_IN:
                    camera.center = where;
                    camera.zoom(zoomStep);
                    break;
                case Navigation.ZOOM_OUT:
                    camera.center = where;
                    camera.zoom(1.0 / zoomStep);
                    break;
                case Navigation.PAN:
                    camera.center = where;
                    break;
            }

            var pixel:Point = SRS.fromWhereToPixel(where, map);
            map.layers.x += 0.5 * map.width - pixel.x;
            map.layers.y += 0.5 * map.height - pixel.y;
            map.camera = camera;
        }

        public final function panInDirection(direction:int):void
        {
            if (!map)
            {
                return;
            }
            var camera:PanZoom = PanZoom(map.camera).clone();
            switch (direction)
            {
                case Navigation.PAN_DOWN:
                    map.layers.y += panStep / camera.scale;
                    camera.pan(0, -panStep);
                    break;
                case Navigation.PAN_LEFT:
                    map.layers.x += -panStep / camera.scale;
                    camera.pan(-panStep, 0);
                    break;
                case Navigation.PAN_RIGHT:
                    map.layers.x += panStep / camera.scale;
                    camera.pan(panStep, 0);
                    break;
                case Navigation.PAN_UP:
                    map.layers.y += -panStep / camera.scale;
                    camera.pan(0, panStep);
                    break;
            }
            map.camera = camera;
        }

        public final function get panStep():Number
        {
            return _panStep;
        }

        public final function set panStep(value:Number):void
        {
            _panStep = value;
        }

        public final function resetCamera():void
        {
            var camera:PanZoom = PanZoom(map.cameraDefault).clone();
            map.camera = camera;
        }

        public final function zoom(zoomType:int, anchor:OGCPoint = null):void
        {
            if (!map)
            {
                return;
            }
            var scale:Number = 1.0;
            switch (zoomType)
            {
                case Navigation.ZOOM_IN:
                    scale = zoomStep;
                    break;
                case Navigation.ZOOM_OUT:
                    scale = 1.0 / zoomStep;
                    break;
            }

            var v_map:MapValue = map.value;
            var v_map_afterZoom:MapValue = new MapValue(v_map.width, v_map.height, v_map.SRID, v_map.centerX, v_map.centerY, scale * v_map.scale);
            var camera:PanZoom = PanZoom(map.camera).clone();
            camera.zoom(scale);
            if (anchor)
            {
                var pixel:Point = map.fromWhereToPixel(anchor);
                var anchor_afterZoom:OGCPoint = SRS.fromPixelToWhere(pixel, v_map_afterZoom);
                camera.pan(anchor_afterZoom.x - anchor.x, anchor_afterZoom.y - anchor.y);
                var pixel_afterZoom:Point = SRS.fromWhereToPixel(anchor, v_map_afterZoom);
                map.layers.x += (pixel.x - pixel_afterZoom.x) * scale;
                map.layers.y += (pixel.y - pixel_afterZoom.y) * scale;
            }
            map.camera = camera;
        }

        public final function get zoomStep():Number
        {
            return _zoomStep;
        }

        public final function set zoomStep(value:Number):void
        {
            _zoomStep = value;
        }

    }
}